#include "ProjectWriter.h"
#include "../Program.h"
#include "../entity/EntityLight.h"
#include "../entity/EntityCamera.h"
#include "../entity/EntitySceneNode.h"

/**
 * @brief 写入项目文件
 * @param filename
 * @return
 */
bool ProjectWriter::Write(const QString& filename)
{
    QDomDocument doc;
    doc.appendChild(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\""));
    QDomElement project = doc.createElement("project");
    project.setAttribute("version", "1.0");
    doc.appendChild(project);
    writeLight(doc, project);
    writeCamera(doc, project);
    writeSetting(doc, project);
    writeScene(doc, project);
    writeScript(doc, project);
    writeExtra(doc, project);

    QFile file(filename);
    if (file.open(QFileDevice::ReadWrite | QFileDevice::Truncate))
    {
        Program::WorkingSpace = filename;
        QTextStream stream(&file);
        doc.save(stream, 4);
        file.close();
        return true;
    }
    return false;
}

void ProjectWriter::writeLight(QDomDocument& doc, QDomElement& project)
{
    QDomElement light = doc.createElement("light");
    project.appendChild(light);
    light.setAttribute("type", Program::LightParam.ToString());
    QDomElement node1 = doc.createElement("position");
    node1.appendChild(doc.createTextNode(Program::LightParam.Position.ToString()));
    light.appendChild(node1);
    QDomElement node2 = doc.createElement("direction");
    node2.appendChild(doc.createTextNode(Program::LightParam.Direction.ToString()));
    light.appendChild(node2);
    QDomElement node3 = doc.createElement("range");
    node3.appendChild(doc.createTextNode(QString::number(Program::LightParam.Range)));
    light.appendChild(node3);
    QDomElement node4 = doc.createElement("angle");
    node4.appendChild(doc.createTextNode(QString::number(Program::LightParam.Angle)));
    light.appendChild(node4);
    QDomElement node5 = doc.createElement("color");
    node5.appendChild(doc.createTextNode(Program::LightParam.Color.ToString()));
    light.appendChild(node5);
    QDomElement node6 = doc.createElement("bind_camera");
    node6.appendChild(doc.createTextNode(Program::LightParam.BindCamera ? "true" : "false"));
    light.appendChild(node6);
}

void ProjectWriter::writeCamera(QDomDocument& doc, QDomElement& project)
{
    QDomElement camera = doc.createElement("camera");
    project.appendChild(camera);
    camera.setAttribute("type", Program::CameraParam.ToString());
    QDomElement node1 = doc.createElement("position");
    node1.appendChild(doc.createTextNode(Program::CameraParam.Position.ToString()));
    camera.appendChild(node1);
    QDomElement node2 = doc.createElement("angle");
    node2.appendChild(doc.createTextNode(Program::CameraParam.Angle.ToString()));
    camera.appendChild(node2);
    QDomElement node3 = doc.createElement("target");
    node3.appendChild(doc.createTextNode(Program::CameraParam.Target.ToString()));
    camera.appendChild(node3);
    QDomElement node4 = doc.createElement("fov");
    node4.appendChild(doc.createTextNode(QString::number(Program::CameraParam.FieldOfView)));
    camera.appendChild(node4);
    QDomElement node5 = doc.createElement("near");
    node5.appendChild(doc.createTextNode(QString::number(Program::CameraParam.ClipNear)));
    camera.appendChild(node5);
    QDomElement node6 = doc.createElement("far");
    node6.appendChild(doc.createTextNode(QString::number(Program::CameraParam.ClipFar)));
    camera.appendChild(node6);
}

void ProjectWriter::writeSetting(QDomDocument& doc, QDomElement& project)
{
    QDomElement setting = doc.createElement("setting");
    project.appendChild(setting);
    QDomElement node1 = doc.createElement("background");
    node1.appendChild(doc.createTextNode(Program::Config.Background.ToString()));
    setting.appendChild(node1);
    QDomElement node2 = doc.createElement("gravity");
    node2.appendChild(doc.createTextNode(Program::Config.Gravity.ToString()));
    setting.appendChild(node2);
    QDomElement node3 = doc.createElement("input_mouse_map");
    node3.appendChild(doc.createTextNode(QString("%1,%2,%3")
                                             .arg(Program::Config.InputMouseMap[0])
                                             .arg(Program::Config.InputMouseMap[1])
                                             .arg(Program::Config.InputMouseMap[2])));
    setting.appendChild(node3);
    QDomElement node4 = doc.createElement("input_speed");
    node4.appendChild(doc.createTextNode(QString("%1,%2").arg(Program::Config.InputSpeed[0]).arg(Program::Config.InputSpeed[1])));
    setting.appendChild(node4);
    QDomElement node5 = doc.createElement("input_disable");
    node5.appendChild(doc.createTextNode(QString("%1,%2,%3")
                                             .arg(Program::Config.InputDisable[0] ? "true" : "false",
                                                  Program::Config.InputDisable[1] ? "true" : "false",
                                                  Program::Config.InputDisable[2] ? "true" : "false")));
    setting.appendChild(node5);
    QDomElement node6 = doc.createElement("plugin");
    node6.appendChild(doc.createTextNode(Program::Config.PluginUsed.join(",")));
    setting.appendChild(node6);
}

void ProjectWriter::writeScene(QDomDocument& doc, QDomElement& project)
{
    QDomElement scene = doc.createElement("scene");
    project.appendChild(scene);
    QMapIterator<QString, std::shared_ptr<EntitySceneNode>> iter(Program::SceneNodeList);
    while (iter.hasNext())
    {
        iter.next();
        const EntitySceneNode* sceneNode = iter.value().get();
        QDomElement item = doc.createElement("node");
        scene.appendChild(item);
        item.setAttribute("name", sceneNode->Name);
        item.setAttribute("type", sceneNode->Type);
        item.setAttribute("parent", sceneNode->Parent == nullptr ? "" : sceneNode->Parent->Name);
        QDomElement node1 = doc.createElement("attribute");
        node1.appendChild(doc.createTextNode(sceneNode->Attribute));
        item.appendChild(node1);
        QDomElement node2 = doc.createElement("position");
        node2.appendChild(doc.createTextNode(sceneNode->Position.ToString()));
        item.appendChild(node2);
        QDomElement node3 = doc.createElement("angle");
        node3.appendChild(doc.createTextNode(sceneNode->Angle.ToString()));
        item.appendChild(node3);
        QDomElement node4 = doc.createElement("scale");
        node4.appendChild(doc.createTextNode(sceneNode->Scale.ToString()));
        item.appendChild(node4);
        QDomElement node5 = doc.createElement("visible");
        node5.appendChild(doc.createTextNode(sceneNode->Visible ? "true" : "false"));
        item.appendChild(node5);
        QDomElement node6 = doc.createElement("physical");
        node6.setAttribute("enable", sceneNode->Physical.Enable ? "true" : "false");
        if (sceneNode->Physical.Enable)
        {
            QDomElement subject1 = doc.createElement("trigger");
            subject1.appendChild(doc.createTextNode(sceneNode->Physical.Trigger ? "true" : "false"));
            node6.appendChild(subject1);
            QDomElement subject2 = doc.createElement("allow_sleep");
            subject2.appendChild(doc.createTextNode(sceneNode->Physical.AllowSleep ? "true" : "false"));
            node6.appendChild(subject2);
            QDomElement subject3 = doc.createElement("shape");
            subject3.appendChild(doc.createTextNode(sceneNode->Physical.ShapeList.join(',')));
            node6.appendChild(subject3);
            QDomElement subject4 = doc.createElement("mass");
            subject4.appendChild(doc.createTextNode(QString::number(sceneNode->Physical.Mass)));
            node6.appendChild(subject4);
            QDomElement subject5 = doc.createElement("linear_damping");
            subject5.appendChild(doc.createTextNode(QString::number(sceneNode->Physical.LinearDamping)));
            node6.appendChild(subject5);
            QDomElement subject6 = doc.createElement("angular_damping");
            subject6.appendChild(doc.createTextNode(QString::number(sceneNode->Physical.AngularDamping)));
            node6.appendChild(subject6);
            QDomElement subject7 = doc.createElement("friction");
            subject7.appendChild(doc.createTextNode(QString::number(sceneNode->Physical.Friction)));
            node6.appendChild(subject7);
            QDomElement subject8 = doc.createElement("restitution");
            subject8.appendChild(doc.createTextNode(QString::number(sceneNode->Physical.Restitution)));
            node6.appendChild(subject8);
        }
        item.appendChild(node6);
        QDomElement node7 = doc.createElement("material");
        node7.setAttribute("enable", sceneNode->Material.Enable ? "true" : "false");
        node7.setAttribute("cull_face", sceneNode->Material.IsCullFace ? "true" : "false");
        node7.setAttribute("add_color", sceneNode->Material.IsAddColor ? "true" : "false");
        node7.setAttribute("use_depth", sceneNode->Material.IsUseDepth ? "true" : "false");
        node7.setAttribute("wireframe", sceneNode->Material.IsWireframe ? "true" : "false");
        if (sceneNode->Material.Enable)
        {
            QDomElement subject1 = doc.createElement("roughness");
            subject1.appendChild(doc.createTextNode(QString::number(sceneNode->Material.Roughness)));
            node7.appendChild(subject1);
            QDomElement subject2 = doc.createElement("metalness");
            subject2.appendChild(doc.createTextNode(QString::number(sceneNode->Material.Metalness)));
            node7.appendChild(subject2);
            QDomElement subject3 = doc.createElement("color");
            subject3.appendChild(doc.createTextNode(sceneNode->Material.Color));
            node7.appendChild(subject3);
            QDomElement subject4 = doc.createElement("emissive");
            subject4.appendChild(doc.createTextNode(QString::number(sceneNode->Material.Emissive)));
            node7.appendChild(subject4);
        }
        item.appendChild(node7);
        QDomElement node8 = doc.createElement("script");
        node8.appendChild(doc.createTextNode(sceneNode->ExtraScript.join('\n').toUtf8().toBase64()));
        item.appendChild(node8);
    }
}

void ProjectWriter::writeScript(QDomDocument& doc, QDomElement& project)
{
    QDomElement script = doc.createElement("script");
    project.appendChild(script);
    for (qsizetype i = 0; i < Program::UserScript.size(); i++)
    {
        const QPair<QString, QString>& item = Program::UserScript.at(i);
        QDomElement node1 = doc.createElement("text");
        node1.setAttribute("name", item.first);
        node1.appendChild(doc.createTextNode(item.second.toUtf8().toBase64()));
        script.appendChild(node1);
    }
}

void ProjectWriter::writeExtra(QDomDocument& doc, QDomElement& project)
{
    QDomElement extra = doc.createElement("extra");
    project.appendChild(extra);
    QDomElement node1 = doc.createElement("track");
    node1.appendChild(doc.createTextNode(Program::LineTrack));;
    extra.appendChild(node1);
}
